package bman.game.engine;

import java.awt.Point;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Reader;
import java.rmi.server.UID;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.logging.Logger;

import bman.tools.JFile;
import bman.tools.net.Receiver;


public class GameMap
{
	Logger log = Logger.getLogger(getClass().getName());
	
	int m_width;
	int m_height;
	
	//SortedMap layers = new TreeMap();
	Map<String,Entity> allEntities = new HashMap<String,Entity>();
	
	/**
	 * This is the total number of Entities that has been created on a JVM.
	 */
	private static int count = 0;
	
	/**
	 * This is the correct way of creating an Entity. The created entity
	 * will is not yet added to the GameMap instance.
	 * The GameMap will generate a unique ID for the newly created entity. 
	 */
	public Entity createEntity(String type, int x, int y, int z)
	{
		return new Entity(generateID(),type,x,y,z);
	}
	
	/**
	 * The GameMap will generate a unique ID for the newly created entity.
	 * The newly created entity will be added into the GameMap
	 * ImageLibrary should contain the entity type. The entity type is not
	 * case sensitive.
	 * 
	 * @param type The entity type. This is expected to be the image key loaded into
	 * the ImageLibrary. The entity type is not case sensitive.
	 */
	public Entity addEntity(String type, int x, int y, int z)
	{
		Entity e = createEntity(type,x,y,z);
		this.add(e);
		return e;
	}

	public String generateID()
	{
		UID u = new UID();
		String id = u.toString();
		while(allEntities.containsKey(id) == true)
		{
			id = String.valueOf(count++);
		}
		return id;
	}
	
	public void clear()
	{
		allEntities.clear();
	}
	
	public void setSize(int width, int height)
	{
		m_width = width;
		m_height = height;
	}
	
	public int width()
	{
		return m_width;
	}
	
	public int height()
	{
		return m_height;
	}
	
	public void remove(Entity e)
	{
		log.info("Removing: "+e.toString());
		allEntities.remove(e.getID());
	}
	
	/**
	 * Adds an entity into the Map. Arranges the entities into layers according to their z axis
	 */
	public void add(Entity e)
	{
		log.info("Adding "+e.toString());
		allEntities.put(e.getID(),e);
	}
	
	public Entity getEntity(String id)
	{
		return allEntities.get(id);
	}
	
	public List getEntiteis(int layer)
	{
		List l =  new ArrayList();
		
		Collection<Entity> c = allEntities.values();
		Iterator<Entity> i = c.iterator();
		while(i.hasNext())
		{
			Entity e = i.next();
			if(e.z() == layer)
			{
				l.add(e);
			}
		}
		
		return l;
	}
	
	/**
	 * @return Returns a list of entities sorted by their z value (i.e Entity.z())
	 */
	public List getEntities()
	{
		SortedMap layers = new TreeMap();
		
		Collection<Entity> c = allEntities.values();
		Iterator<Entity> i = c.iterator();
		while(i.hasNext())
		{
			Entity e = i.next();
			
			List l = (List)layers.get(e.z());
			if(l == null)
			{
				l = new ArrayList();
				layers.put(e.z(),l);
			}
			
			l.add(e);
		}
		
		List all = new ArrayList();
		Iterator i2 = layers.values().iterator();
		while(i2.hasNext())
		{
			List layer = (List)i2.next();
			all.addAll(layer);
		}
		
		return all;
	}
	
	public boolean load(Reader reader)
	{
		try
		{
			BufferedReader in = new BufferedReader(reader);
			String s = in.readLine();
					
			while(s != null)
			{
				s = s.trim();
				
				if(!s.startsWith("#") && !s.startsWith("version") && s.length() > 0)
				{
					StringTokenizer st = new StringTokenizer(s,",");
					log.fine("processing: "+s);

					String id, x, y, z;
					String movable = null;

					id = st.nextToken();
					x = st.nextToken();
					y = st.nextToken();
					z = st.nextToken();
					
					try
					{
						movable = st.nextToken();
					}
					catch(NoSuchElementException e)
					{
						log.warning("Optional attributes missing in entry: "+s);
					}
					

					Entity e = addEntity(id, Integer.parseInt(x),
												Integer.parseInt(y),
												Integer.parseInt(z));
					
					e.movable(Boolean.parseBoolean(movable));
					
					log.info("Loaded (id,x,y,z) "+id+","+x+","+y+","+z);
				}
				
				s = in.readLine();
			}
			return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
			log.warning("Error loading file");
			return false;
		}
		
	}
	
	public boolean load(String file)
	{
		log.info("Loading file "+file);
		try
		{
			FileReader fr = new FileReader(file);
			return this.load(fr);
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
			log.warning("Error loading file");
			return false;
		}
	}
	
	public boolean hasReachedBorder(Entity e)
	{
		int x = e.x();
		int y = e.y();
		int width = e.width();
		int height = e.height();
		
		return (x < 0 || y < 0 || (x + width- width()) > 0 || (y +height- height()) > 0 );
	}
	
}
